package prototyping.server;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import javax.swing.JFrame;

import dao.DataConnection;
import prototyping.client.AbstractMessage;
import prototyping.client.ChatMessage;

public class Server extends DataConnection{

	// a unique ID for each connection
	// Use a map collection instead?? Unique id is de Key, ClientThread is the
	// value.
	private static int uniqueId;
	// An ArrayList to keep the list of the Client
	// TODO Change this to a map??
	private ArrayList<ClientListener> clientListeners;
	private SimpleDateFormat sdf;
	private int port = 1500;
	private boolean keepRunning;

	public Server() {
		this(null);
	}

	/*
	 * If you want to use a frame this constructor can be used. Don't forget the
	 * variable!
	 */
	public Server(JFrame sg) {
		sdf = new SimpleDateFormat("HH:mm:ss");
		clientListeners = new ArrayList<ClientListener>();
	}

	public Server(int port) {
		this(port, null);
	}

	/*
	 * If you want to use a frame this constructor can be used. Don't forget the
	 * variable!
	 */
	public Server(int port, JFrame sg) {
		this.port = port;
		sdf = new SimpleDateFormat("HH:mm:ss");
		clientListeners = new ArrayList<ClientListener>();
	}

	public void start() {
		keepRunning = true;
		try {
			ServerSocket serverSocket = new ServerSocket(port);
			System.out.println("Server started!");
			
			/*
			 * Infinite loop waiting for connections. If a client makes a socket
			 * to this server and port the socket is accepted and a connection
			 * established. If no connection is available, the method blocks
			 * until there is a connection.
			 */
			while (keepRunning) {
				Socket socket = serverSocket.accept();
				if (!keepRunning) {
					break;
				}
				ClientListener cl = new ClientListener(socket, this, uniqueId);
				clientListeners.add(cl);
				cl.start();
			}
			/*
			 * The method only continues if keeprunning = false.
			 */
			try {
				serverSocket.close();
				for (int i = 0; i < clientListeners.size(); ++i) {
					ClientListener cl = clientListeners.get(i);
					try {
						cl.getsInput().close();
						cl.getsOutput().close();
						cl.getSocket().close();
					} catch (IOException ioE) {
						ioE.printStackTrace();
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (IOException e) {
			String msg = sdf.format(new Date())
					+ " Exception on new ServerSocket: " + e + "\n";
			try {
				broadcast(new ChatMessage(msg));
			} catch (UnknownHostException e1) {
				e1.printStackTrace();
			}
		}
	}

	/*
	 * What the F*** is this???
	 */
	protected void stop() {
		keepRunning = false;
		// TODO connect to myself as Client to exit statement
		try {
			new Socket("localhost", port);
		} catch (Exception e) {
			// TODO nothing I can really do
		}
	}

	/*
	 * Displays server events in either console or GUI mode.
	 * Maybe only for testing purposes. Probably write events to log file.
	 */
	// TODO Verify what to do exactly with this method
	private void display(AbstractMessage amsg) {
		// TODO Change and use GregorianCalendar.
		String time = sdf.format(new Date()) + " " + amsg;
		System.out.println(time);
	}

	/*
	 * To broadcast a message to all Clients Should be separated to broadcasting
	 * to all clients and just to TeacherClients??
	 */
	// TODO Study and adjust if necessary
	public synchronized void broadcast(AbstractMessage aMessage) {
		// TODO Change and use GregorianCalendar.
		String time = sdf.format(new Date());
		String messageLf = time + " " + aMessage;
		aMessage.setMessage(messageLf);

		/*
		 * Loop in reverse order, in case a clientlistener is removed. Better to
		 * use iterator.
		 */
		for (int i = clientListeners.size(); --i >= 0;) {
			ClientListener cl = clientListeners.get(i);
			// try to write to the Client if it fails remove it from the list
			if (!cl.writeMsg(aMessage)) {
				clientListeners.remove(i);
				try {
					display(new ChatMessage("Disconnected Client " + cl.getUsername()
							+ " removed from list."));
				} catch (UnknownHostException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/*
	 * In case a client disconnects and should be removed from clientlisteners.
	 */
	public synchronized void remove(int id) {
		/*
		 * TODO better to use other collection witch captures comparable
		 * objects. Maybe not necessary when using Map.
		 */
		for (int i = 0; i < clientListeners.size(); ++i) {
			ClientListener cl = clientListeners.get(i);
			if (cl.getId() == id) {
				clientListeners.remove(i);
				return;
			}
		}
	}

}
